﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.IO;
using SSharp.Core.Reader;
using SSharp.Core.DataTypes;

namespace SSharp.Test {
	[TestClass]
	public class TestReader {
		private const string TestString = @"
a
1
2.
3.4
(1 2)
[a b]
{1 2.0 c}
; comment
'a
`1
,(1 2)
""c""
""ab\\cd\""ef'gh""
(define (square x) (* x x))
";
		private const string WithDots = @"
(a b)			; (list 'a 'b)
(a . b)			; (cons 'a 'b)
(a b . c)		; (cons 'a (cons 'b 'c))
(a . b . c)		; (list 'b 'a 'c)
";

		[TestMethod]
		public void TestSchemeReader() {
			using (MemoryStream stream = new MemoryStream())
			using (StreamWriter writer = new StreamWriter(stream)) {
				writer.Write(TestString);
				writer.Flush();
				stream.Seek(0, SeekOrigin.Begin);
				using (SchemeReader reader = new SchemeReader(stream)) {
					object value = reader.ReadExpression();
					Assert.AreEqual(s("a"), value);
					Console.WriteLine(value);

					value = reader.ReadExpression();
					Assert.AreEqual(1, value);
					Console.WriteLine(value);

					value = reader.ReadExpression();
					Assert.AreEqual(2.0m, value);
					Console.WriteLine(value);

					value = reader.ReadExpression();
					Assert.AreEqual(3.4m, value);
					Console.WriteLine(value);

					value = reader.ReadExpression();
					Assert.AreEqual(List.Create(1, 2), value);
					Console.WriteLine(value);

					value = reader.ReadExpression();
					Assert.AreEqual(List.Create(s("a"), s("b")), value);
					Console.WriteLine(value);

					value = reader.ReadExpression();
					Assert.AreEqual(List.Create(1, 2.0m, s("c")), value);
					Console.WriteLine(value);

					value = reader.ReadExpression();
					Assert.AreEqual(List.Create(s("quote"), s("a")), value);
					Console.WriteLine(value);

					value = reader.ReadExpression();
					Assert.AreEqual(List.Create(s("quasiquote"), 1), value);
					Console.WriteLine(value);

					value = reader.ReadExpression();
					Assert.AreEqual(List.Create(s("unquote"), List.Create(1, 2)), value);
					Console.WriteLine(value);

					value = reader.ReadExpression();
					Assert.AreEqual("c", value);
					Console.WriteLine(value);

					value = reader.ReadExpression();
					Assert.AreEqual("ab\\cd\"ef'gh", value);
					Console.WriteLine(value);

					value = reader.ReadExpression();
					Assert.AreEqual(List.Create(s("define"), List.Create(s("square"), s("x")), List.Create(s("*"), s("x"), s("x"))), value);
					Console.WriteLine(value);

					value = reader.ReadExpression();
					Assert.IsNull(value);
					Console.WriteLine(value);
				}
			}
		}

		[TestMethod]
		public void TestCharReaderBlockMode() {
			using (MemoryStream s = new MemoryStream())
			using (StreamWriter writer = new StreamWriter(s)) {
				writer.Write(TestString);
				writer.Flush();
				s.Seek(0, SeekOrigin.Begin);
				using (CharReader reader = new CharReader(s)) {
					reader.Mode = CharReader.CharReaderMode.Block;
					int index = 0;
					foreach (char c in reader.GetChars()) {
						Assert.AreEqual(TestString[index], c);
						index++;
					}
					Assert.AreEqual(TestString.Length, index);
				}
			}
		}

		[TestMethod]
		public void TestcharReaderLineMode() {
			using (MemoryStream s = new MemoryStream())
			using (StreamWriter writer = new StreamWriter(s)) {
				writer.Write(TestString);
				writer.Flush();
				s.Seek(0, SeekOrigin.Begin);
				using (CharReader reader = new CharReader(s)) {
					reader.Mode = CharReader.CharReaderMode.Line;
					int index = 0;
					foreach (char c in reader.GetChars()) {
						while (TestString[index] == '\r') {
							// line mode drops all '\r's.
							index++;
						}
						Assert.AreEqual(TestString[index], c);
						index++;
					}
					Assert.AreEqual(TestString.Length, index);
				}
			}
		}

		/// <summary>
		/// Convenience method to create a symbol from a string.
		/// </summary>
		private Symbol s(string str) {
			return new Symbol(str);
		}
	}
}
